import { Injectable } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { Like, Repository } from "typeorm";
import { InjectRepository } from "@nestjs/typeorm";
import { User } from "@/modules/user/entities/user.entity";
import { ResultData } from "@/common/utils/result";
import { instanceToPlain } from "class-transformer";
import { AppHttpCode } from "@/enum/code.enum";
import { JwtService } from "@nestjs/jwt";
import { CreateTokenDto } from "@/modules/user/dto/create-token.dto";
import { ConfigService } from "@nestjs/config";

@Injectable()
export class UserService {
	
	constructor(
		@InjectRepository(User)
		private readonly userRepository: Repository<User>,
		private readonly jwtService: JwtService,
		private readonly config: ConfigService
	) {
	}
	
	async create(createUserDto: CreateUserDto) {
		// 防止重复创建 start
		if (await this.findOneByUsername(createUserDto.username)) return ResultData.fail(AppHttpCode.USER_CREATE_EXISTING, "帐号已存在，请调整后重新注册！");
		return ResultData.ok(instanceToPlain(await this.userRepository.save(createUserDto)));
	}
	
	async findOneByUsername(username: string) {
		return await this.userRepository.findOne({ where: { username } });
	}
	
	async findOneById(id: string) {
		return await this.userRepository.findOneById(id);
	}
	
	async findAll(dto) {
		const { page, size, username, status } = dto;
		const take = size || 15;
		const skip = ((page || 1) - 1) * take;
		const where = {
			...(status ? { status } : null),
			...(username ? { username: Like(`%${username}%`) } : null)
		};
		return ResultData.rowOk(instanceToPlain(await this.userRepository.findAndCount({
			order: {
				id: "DESC"
			},
			where,
			select: {
				userRole: {
					id: true,
					role: {
						id: true,
						name: true,
						remark: true
					}
				}
			},
			relations: ["userRole.role"],
			skip,
			take
		})));
		// return this.userRepository.createQueryBuilder("user")
		// 	.orderBy("user.id", "DESC")
		// 	.leftJoinAndSelect("user.userRole", "userRole")
		// 	.leftJoinAndSelect("userRole.role", "role")
		// 	.select(["user.id", "user.username"])
		// 	.addSelect(["userRole.id"])
		// 	.addSelect(["role.name"])
		// 	// .where(`user.username like :username`, { username: `%${username}%` })
		// 	.andWhere(`user.status = :status`, { status: status?.trim() })
		// 	.skip(skip)
		// 	.take(take)
		// 	.getMany();
	}
	
	async update(id: number, updateUserDto: UpdateUserDto) {
		return ResultData.ok(await this.userRepository.update(id, updateUserDto), "更新成功");
	}
	
	async remove(id: number) {
		return ResultData.ok(await this.userRepository.delete(id), "删除成功");
	}
	
	/**
	 * 生成 token 与 刷新 token
	 * @param payload
	 * @returns
	 */
	genToken(payload: { id: string | number }): CreateTokenDto {
		const accessToken = `Bearer ${this.jwtService.sign(payload)}`;
		const refreshToken = this.jwtService.sign(payload, { expiresIn: this.config.get("JWT_REFRESH_EXPIRESIN") });
		return { accessToken, refreshToken };
	}
	
	/**
	 * 生成刷新 token
	 */
	refreshToken(id: string): string {
		return this.jwtService.sign({ id });
	}
	
	/** 校验 token */
	verifyToken(token: string): string {
		try {
			if (!token) return null;
			const id = this.jwtService.verify(token.replace("Bearer ", ""));
			return id;
		} catch (error) {
			return null;
		}
	}
	
	/**
	 * 登录
	 * username 有可能是 帐号/手机/邮箱
	 */
	async login(username: string, password: string): Promise<ResultData> {
		const user = await this.findOneByUsername(username);
		if (!user) return ResultData.fail(AppHttpCode.USER_PASSWORD_INVALID, "帐号或密码错误");
		const checkPassword = password === user.password;
		if (!checkPassword) return ResultData.fail(AppHttpCode.USER_PASSWORD_INVALID, "帐号或密码错误");
		if (user.status === 0) return ResultData.fail(AppHttpCode.USER_ACCOUNT_FORBIDDEN, "您已被禁用，如需正常使用请联系管理员");
		// 生成 token
		const data = this.genToken({ id: user.id });
		return ResultData.ok(data, "登录成功");
	}
	
	async updateToken(userId: string): Promise<ResultData> {
		const data = this.genToken({ id: userId });
		return ResultData.ok(data);
	}
	
	async info(id) {
		return ResultData.ok(instanceToPlain(await this.userRepository.findOneById(id)));
	}
}
